<?php

// @formatter:off
/**
 * @file DImages.php
 * @author Alejandro Dario Simi
 * @date $Date: 2014-06-03 03:40:32 +0000 (Tue, 03 Jun 2014) $
 *
 * $Id: DImages.php 112 2014-06-03 03:40:32Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/trunk/extensions/DuplicatedImages/includes/DImages.php $
 */
// @formatter:on

class DImages extends WCSingleton {
	//
	// Protected variables.
	/**
	 * @var WCConnection Database pointer shortcut.
	 */
	protected $_db;
	/**
	 * @var string Database tables' prefix shortcut.
	 */
	protected $_dbprefix;
	protected $_fingerprintSize = 8;
	protected $_pseudoFingerprints = array(
		"1101111111101110100011110011110110010001111011111010010010101011",
		"1001000000010110100011011001000011111110000101101001111111000101",
		"0110110010101101111000000010011011010101101110101011010101101110",
		"0101110110011100010100011010001100011101011111010101010010100011",
		"1101011110000100010001001100001010101110001011000101010011011101",
		"0111000001110010101100010011000011000111000111000110101011011001",
		"1000000101010010010000100010001010010011000000100010011100101100",
		"0011010101111011000001001111101101111111110010010000010110101100",
		"0101000111001001110000011101011100010111000011100111011110101011",
		"0011100001000011100010000101011100001000010011111001101110111000",
		"1101001000001001100000000110000100001001011100110000000010001011",
		"0001011001011111110001000001111001000101111110110111111100000000"
	);
	protected $_timer = false;
	//
	// Public methods.
	public function comicsByDistance(WCGroup &$group, &$distances = false) {
		$out = array();
		$distances = array();
		//
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  *\n";
			$query.= "from    {$this->_dbprefix}dimages\n";
			$query.= "where   dif_group = :group\n";
			$query.= " and    dif_comic > 0\n";
			$query.= " and    dif_distance in (\n";
			$query.= "                select  did_value\n";
			$query.= "                from    {$this->_dbprefix}dimages_dups\n";
			$query.= "                where   did_group = :group\n";
			$query.= "                 and    did_type  = 'D'\n";
			$query.= "        )\n";
			$query.= "order by dif_distance desc\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		if($stmt->execute(array(":group" => $group->id)) && $stmt->rowCount() > 0) {
			$holder = WCComicsHolder::I();
			foreach($stmt->fetchAll() as $row) {
				if(!isset($out[$row["dif_comic"]])) {
					$cmc = $holder->item($row["dif_comic"]);
					if($cmc && $cmc->ok()) {
						$out[$row["dif_comic"]] = $cmc;
						$distances[$row["dif_comic"]] = array($row["dif_distance"]);
					}
				} else {
					$distances[$row["dif_comic"]][] = $row["dif_distance"];
				}
			}
		}

		return $out;
	}
	public function comicsByFingerprint(WCGroup &$group, &$fingeprint = false) {
		$out = array();
		$fingeprint = array();
		//
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  *\n";
			$query.= "from    {$this->_dbprefix}dimages\n";
			$query.= "where   dif_group = :group\n";
			$query.= " and    dif_comic > 0\n";
			$query.= " and    dif_fingerprint in (\n";
			$query.= "                select  did_value\n";
			$query.= "                from    {$this->_dbprefix}dimages_dups\n";
			$query.= "                where   did_group = :group\n";
			$query.= "                 and    did_type  = 'F'\n";
			$query.= "        )\n";
			$query.= "order by dif_fingerprint desc\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		if($stmt->execute(array(":group" => $group->id)) && $stmt->rowCount() > 0) {
			$holder = WCComicsHolder::I();
			foreach($stmt->fetchAll() as $row) {
				if(!isset($out[$row["dif_comic"]])) {
					$cmc = $holder->item($row["dif_comic"]);
					if($cmc && $cmc->ok()) {
						$out[$row["dif_comic"]] = $cmc;
						$fingeprint[$row["dif_comic"]] = array($row["dif_fingeprint"]);
					}
				} else {
					$fingeprint[$row["dif_comic"]][] = $row["dif_fingeprint"];
				}
			}
		}

		return $out;
	}
	public function comicsByMD5(WCGroup &$group, &$md5 = false) {
		$out = array();
		$md5 = array();
		//
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  {$this->_dbprefix}comic_images.*\n";
			$query.= "from    {$this->_dbprefix}comics\n";
			$query.= "                inner join {$this->_dbprefix}comic_images\n";
			$query.= "                        on(cmc_id = cim_comic)\n";
			$query.= "where   cmc_group = :group\n";
			$query.= " and    cim_md5 in (\n";
			$query.= "                select  did_value\n";
			$query.= "                from    {$this->_dbprefix}dimages_dups\n";
			$query.= "                where   did_group = :group\n";
			$query.= "                 and    did_type  = '5'\n";
			$query.= "        )\n";
			$query.= "order by cim_md5 desc\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		if($stmt->execute(array(":group" => $group->id)) && $stmt->rowCount() > 0) {
			$holder = WCComicsHolder::I();
			foreach($stmt->fetchAll() as $row) {
				if(!isset($out[$row["cim_comic"]])) {
					$cmc = $holder->item($row["cim_comic"]);
					if($cmc && $cmc->ok()) {
						$out[$row["cim_comic"]] = $cmc;
						$md5[$row["cim_comic"]] = array($row["cim_md5"]);
					}
				} else {
					$md5[$row["cim_comic"]][] = $row["cim_md5"];
				}
			}
		}

		return $out;
	}
	public function comicsBySHA1(WCGroup &$group, &$sha1 = false) {
		$out = array();
		$sha1 = array();
		//
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  {$this->_dbprefix}comic_images.*\n";
			$query.= "from    {$this->_dbprefix}comics\n";
			$query.= "                inner join {$this->_dbprefix}comic_images\n";
			$query.= "                        on(cmc_id = cim_comic)\n";
			$query.= "where   cmc_group = :group\n";
			$query.= " and    cim_sha1 in (\n";
			$query.= "                select  did_value\n";
			$query.= "                from    {$this->_dbprefix}dimages_dups\n";
			$query.= "                where   did_group = :group\n";
			$query.= "                 and    did_type  = 'S'\n";
			$query.= "        )\n";
			$query.= "order by cim_sha1 desc\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		if($stmt->execute(array(":group" => $group->id)) && $stmt->rowCount() > 0) {
			$holder = WCComicsHolder::I();
			foreach($stmt->fetchAll() as $row) {
				if(!isset($out[$row["cim_comic"]])) {
					$cmc = $holder->item($row["cim_comic"]);
					if($cmc && $cmc->ok()) {
						$out[$row["cim_comic"]] = $cmc;
						$sha1[$row["cim_comic"]] = array($row["cim_sha1"]);
					}
				} else {
					$sha1[$row["cim_comic"]][] = $row["cim_sha1"];
				}
			}
		}

		return $out;
	}
	public function getFingerprint($path, $fingerprint = false, $relation = false) {
		$out = array(
			"fingerprint" => "",
			"distance" => "",
			"relation" => 0,
			"status" => false,
			"time" => 0
		);
		$this->_timer->start(__CLASS__."::".__FUNCTION__);

		if(!$fingerprint || !$relation) {
			//
			// Getting image path information.
			$imgInfo = @getimagesize($path);
			if($imgInfo !== false) {
				//
				// Opening image.
				$img = null;
				switch($imgInfo["mime"]) {
					case "image/jpeg":
						$img = @imagecreatefromjpeg($path);
						break;
					case "image/png":
						$img = @imagecreatefrompng($path);
						break;
					case "image/gif":
						$img = @imagecreatefromgif($path);
						break;
					default:
						break;
				}
				//
				// Checking if the image was successfully opened.
				if($img) {
					//
					// Removing colors
					imagefilter($img, IMG_FILTER_GRAYSCALE);
					//
					// Calculating size relation.
					$width = imagesx($img);
					$height = imagesy($img);

					//
					// Creating a smaller version to work with.
					$tmp_img = imagecreatetruecolor($this->_fingerprintSize, $this->_fingerprintSize);
					imagecopyresampled($tmp_img, $img, 0, 0, 0, 0, $this->_fingerprintSize, $this->_fingerprintSize, $width, $height);
					//
					// Calculating the mean value.
					$avg = -1;
					for($x = 0; $x < $this->_fingerprintSize; $x++) {
						for($y = 0; $y < $this->_fingerprintSize; $y++) {
							$value = imagecolorat($tmp_img, $x, $y);
							$avg = $avg < 0 ? $value : (($avg + $value) / 2);
						}
					}
					$avg = round($avg);
					//
					// Generating a color based fingerprint.
					$fingerprint = "";
					for($x = 0; $x < $this->_fingerprintSize; $x++) {
						for($y = 0; $y < $this->_fingerprintSize; $y++) {
							$value = imagecolorat($tmp_img, $x, $y);
							$fingerprint.=$value > $avg ? 1 : 0;
						}
					}

					$out["fingerprint"] = $fingerprint;
					$out["relation"] = round(($width / $height) * 100);
					$out["distance"] = $this->getDistance($out["fingerprint"], $out["relation"]);

					$out["status"] = true;
				}
			}
		} else {
			$out["fingerprint"] = $fingerprint;
			$out["relation"] = $relation;
			$out["distance"] = $this->getDistance($out["fingerprint"], $out["relation"]);

			$out["status"] = true;
		}

		$out["time"] = $this->_timer->stop(__CLASS__."::".__FUNCTION__);

		return $out;
	}
	public function imagesByDistance(WCGroup &$group, &$distances = false) {
		$out = array();
		$distances = array();
		//
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  *\n";
			$query.= "from    {$this->_dbprefix}dimages\n";
			$query.= "where   dif_group = :group\n";
			$query.= " and    dif_comic = 0\n";
			$query.= " and    dif_distance in (\n";
			$query.= "                select  did_value\n";
			$query.= "                from    {$this->_dbprefix}dimages_dups\n";
			$query.= "                where   did_group = :group\n";
			$query.= "                 and    did_type  = 'D'\n";
			$query.= "        )\n";
			$query.= "order by dif_distance desc\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		if($stmt->execute(array(":group" => $group->id)) && $stmt->rowCount() > 0) {
			$holder = WCImagesHolder::I();
			foreach($stmt->fetchAll() as $row) {
				$img = $holder->item($row["dif_image"]);
				if($img && $img->ok()) {
					$out[$row["dif_image"]] = $img;
					$distances[$row["dif_image"]] = $row["dif_distance"];
				}
			}
		}

		return $out;
	}
	public function imagesByFingerprint(WCGroup &$group, &$fingeprint = false) {
		$out = array();
		$fingeprint = array();
		//
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  *\n";
			$query.= "from    {$this->_dbprefix}dimages\n";
			$query.= "where   dif_group = :group\n";
			$query.= " and    dif_comic = 0\n";
			$query.= " and    dif_fingerprint in (\n";
			$query.= "                select  did_value\n";
			$query.= "                from    {$this->_dbprefix}dimages_dups\n";
			$query.= "                where   did_group = :group\n";
			$query.= "                 and    did_type  = 'F'\n";
			$query.= "        )\n";
			$query.= "order by dif_fingerprint desc\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		if($stmt->execute(array(":group" => $group->id)) && $stmt->rowCount() > 0) {
			$holder = WCImagesHolder::I();
			foreach($stmt->fetchAll() as $row) {
				$img = $holder->item($row["dif_image"]);
				if($img && $img->ok()) {
					$out[$row["dif_image"]] = $img;
					$fingeprint[$row["dif_image"]] = $row["dif_fingeprint"];
				}
			}
		}

		return $out;
	}
	public function imagesByMD5(WCGroup &$group, &$md5 = false) {
		$out = array();
		$md5 = array();
		//
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  *\n";
			$query.= "from    {$this->_dbprefix}images\n";
			$query.= "where   img_group = :group\n";
			$query.= " and    img_md5 in (\n";
			$query.= "                select  did_value\n";
			$query.= "                from    {$this->_dbprefix}dimages_dups\n";
			$query.= "                where   did_group = :group\n";
			$query.= "                 and    did_type  = '5'\n";
			$query.= "        )\n";
			$query.= "order by img_md5 desc\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		if($stmt->execute(array(":group" => $group->id)) && $stmt->rowCount() > 0) {
			$holder = WCImagesHolder::I();
			foreach($stmt->fetchAll() as $row) {
				$img = $holder->item($row["img_id"]);
				if($img && $img->ok()) {
					$out[$row["img_id"]] = $img;
					$md5[$row["img_id"]] = $row["img_md5"];
				}
			}
		}

		return $out;
	}
	public function imagesBySHA1(WCGroup &$group, &$sha1 = false) {
		$out = array();
		$sha1 = array();
		//
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  *\n";
			$query.= "from    {$this->_dbprefix}images\n";
			$query.= "where   img_group = :group\n";
			$query.= " and    img_sha1 in (\n";
			$query.= "                select  did_value\n";
			$query.= "                from    {$this->_dbprefix}dimages_dups\n";
			$query.= "                where   did_group = :group\n";
			$query.= "                 and    did_type  = 'S'\n";
			$query.= "        )\n";
			$query.= "order by img_sha1 desc\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		if($stmt->execute(array(":group" => $group->id)) && $stmt->rowCount() > 0) {
			$holder = WCImagesHolder::I();
			foreach($stmt->fetchAll() as $row) {
				$img = $holder->item($row["img_id"]);
				if($img && $img->ok()) {
					$out[$row["img_id"]] = $img;
					$sha1[$row["img_id"]] = $row["img_sha1"];
				}
			}
		}

		return $out;
	}
	//
	// Protected methods.
	public function getDistance($fingerprint, $relation) {
		$out = "";

		$out = sprintf("%03d", $relation);
		//
		// Calculating distances between the color based fingerprint and
		// other pseudo-fingerprints.
		$pixels = $this->_fingerprintSize * $this->_fingerprintSize;
		foreach($this->_pseudoFingerprints as $id => $finger) {
			$count = 0;

			for($i = 0; $i < $pixels; $i++) {
				$count+=$fingerprint[$i] == $finger[$i] ? 1 : 0;
			}

			$out.= sprintf("%02d", $count);
		}

		return $out;
	}
	protected function init() {
		parent::init();
		//
		// Loading shortcuts.
		// @{
		$this->_db = WCConnection::Instance();
		$this->_dbprefix = $this->_db->prefix();
		$this->_timer = WCTimer::I();
		// @}
	}
}
